package com.mia.craftstudio.minecraft;

import com.mia.craftstudio.CSModel;
import com.mia.craftstudio.libgdx.Matrix4;
import com.mia.craftstudio.libgdx.Quaternion;
import com.mia.craftstudio.libgdx.Vector3;
import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;

public class CraftStudioModelWrapper {
   protected final ModelMetadata metadata;
   protected final Vector3[][] extend = new Vector3[16][2];
   protected final int[][] extendBlock = new int[16][6];
   protected final Vector3[][] extendPlacement = new Vector3[16][2];
   protected final int[][] extendPlacementBlock = new int[16][6];
   public Color colorPrimary;
   public Color colorSecondary;
   public Color colorAccentPrimary;
   public Color colorAccentSecondary;
   public final CraftStudioModelWrapper.NodeHashCache nodeCache = new CraftStudioModelWrapper.NodeHashCache(this);

   public CraftStudioModelWrapper(ModelMetadata metadata) {
      metadata.wrapper = this;
      this.metadata = metadata;

      for(int orient = 0; orient < 16; ++orient) {
         this.computeExtend(orient);
         this.computeExtendBlock(orient);
      }

      metadata.wrapperCallback();
   }

   private void computeExtend(int orient) {
      Iterator var2 = this.metadata.csmodel.getTopNodes().iterator();

      CSModel.ModelNode node;
      while(var2.hasNext()) {
         node = (CSModel.ModelNode)var2.next();
         Matrix4 matrix = new Matrix4();
         Matrix4 centerBlock = new Matrix4();
         centerBlock.translate(0.5F, 0.0F, 0.5F);
         matrix.mul(centerBlock);
         Matrix4 scaleModel = new Matrix4();
         scaleModel.setToScaling(this.metadata.scale, this.metadata.scale, this.metadata.scale);
         matrix.mul(scaleModel);
         Matrix4 invertAxe = new Matrix4();
         invertAxe.rotate(new Vector3(0.0F, 0.0F, 1.0F), 180.0F);
         matrix.mul(invertAxe);
         Matrix4 rotation = new Matrix4();
         rotation.rotate(new Vector3(0.0F, 1.0F, 0.0F), (float)orient * 360.0F / 16.0F);
         matrix.mul(rotation);
         this.nodeCache.get(node).computeVertices(matrix, orient);
      }

      this.extend[orient][0] = new Vector3(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
      this.extend[orient][1] = new Vector3(-3.4028235E38F, -3.4028235E38F, -3.4028235E38F);
      this.extendPlacement[orient][0] = new Vector3(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
      this.extendPlacement[orient][1] = new Vector3(-3.4028235E38F, -3.4028235E38F, -3.4028235E38F);
      var2 = this.metadata.csmodel.getNodes().iterator();

      while(true) {
         int i;
         do {
            do {
               do {
                  if (!var2.hasNext()) {
                     return;
                  }

                  node = (CSModel.ModelNode)var2.next();
               } while(node.isNull());

               for(i = 0; i < 8; ++i) {
                  this.extend[orient][0].x = Math.min(this.nodeCache.get(node).getVertices(orient)[i].x, this.extend[orient][0].x);
                  this.extend[orient][1].x = Math.max(this.nodeCache.get(node).getVertices(orient)[i].x, this.extend[orient][1].x);
                  this.extend[orient][0].y = Math.min(this.nodeCache.get(node).getVertices(orient)[i].y, this.extend[orient][0].y);
                  this.extend[orient][1].y = Math.max(this.nodeCache.get(node).getVertices(orient)[i].y, this.extend[orient][1].y);
                  this.extend[orient][0].z = Math.min(this.nodeCache.get(node).getVertices(orient)[i].z, this.extend[orient][0].z);
                  this.extend[orient][1].z = Math.max(this.nodeCache.get(node).getVertices(orient)[i].z, this.extend[orient][1].z);
               }
            } while(node.hasAttribute(CSModel.ModelNode.Attrb.PASSABLE));
         } while(node.hasAttribute(CSModel.ModelNode.Attrb.PASSABLEPROPAGATES));

         for(i = 0; i < 8; ++i) {
            this.extendPlacement[orient][0].x = Math.min(this.nodeCache.get(node).getVertices(orient)[i].x, this.extendPlacement[orient][0].x);
            this.extendPlacement[orient][1].x = Math.max(this.nodeCache.get(node).getVertices(orient)[i].x, this.extendPlacement[orient][1].x);
            this.extendPlacement[orient][0].y = Math.min(this.nodeCache.get(node).getVertices(orient)[i].y, this.extendPlacement[orient][0].y);
            this.extendPlacement[orient][1].y = Math.max(this.nodeCache.get(node).getVertices(orient)[i].y, this.extendPlacement[orient][1].y);
            this.extendPlacement[orient][0].z = Math.min(this.nodeCache.get(node).getVertices(orient)[i].z, this.extendPlacement[orient][0].z);
            this.extendPlacement[orient][1].z = Math.max(this.nodeCache.get(node).getVertices(orient)[i].z, this.extendPlacement[orient][1].z);
         }
      }
   }

   private void computeExtendBlock(int orient) {
      float[] trunc = new float[]{(float)((int)(this.extend[orient][0].x * 1000.0F)) / 1000.0F, (float)((int)(this.extend[orient][0].y * 1000.0F)) / 1000.0F, (float)((int)(this.extend[orient][0].z * 1000.0F)) / 1000.0F, (float)((int)(this.extend[orient][1].x * 1000.0F)) / 1000.0F, (float)((int)(this.extend[orient][1].y * 1000.0F)) / 1000.0F, (float)((int)(this.extend[orient][1].z * 1000.0F)) / 1000.0F};
      this.extendBlock[orient][0] = (int)Math.floor((double)trunc[0]);
      this.extendBlock[orient][1] = (int)Math.floor((double)trunc[1]);
      this.extendBlock[orient][2] = (int)Math.floor((double)trunc[2]);
      this.extendBlock[orient][3] = (int)Math.ceil((double)(trunc[3] - 1.0F));
      this.extendBlock[orient][4] = (int)Math.ceil((double)(trunc[4] - 1.0F));
      this.extendBlock[orient][5] = (int)Math.ceil((double)(trunc[5] - 1.0F));
      float[] truncPlacement = new float[]{(float)((int)(this.extendPlacement[orient][0].x * 1000.0F)) / 1000.0F, (float)((int)(this.extendPlacement[orient][0].y * 1000.0F)) / 1000.0F, (float)((int)(this.extendPlacement[orient][0].z * 1000.0F)) / 1000.0F, (float)((int)(this.extendPlacement[orient][1].x * 1000.0F)) / 1000.0F, (float)((int)(this.extendPlacement[orient][1].y * 1000.0F)) / 1000.0F, (float)((int)(this.extendPlacement[orient][1].z * 1000.0F)) / 1000.0F};
      this.extendPlacementBlock[orient][0] = (int)Math.floor((double)truncPlacement[0]);
      this.extendPlacementBlock[orient][1] = (int)Math.floor((double)truncPlacement[1]);
      this.extendPlacementBlock[orient][2] = (int)Math.floor((double)truncPlacement[2]);
      this.extendPlacementBlock[orient][3] = (int)Math.ceil((double)(truncPlacement[3] - 1.0F));
      this.extendPlacementBlock[orient][4] = (int)Math.ceil((double)(truncPlacement[4] - 1.0F));
      this.extendPlacementBlock[orient][5] = (int)Math.ceil((double)(truncPlacement[5] - 1.0F));
   }

   public Vector3[] getExtend(int orient) {
      return this.extend[orient];
   }

   public int[] getExtendBlock(int orient) {
      return this.extendBlock[orient];
   }

   public int[] getExtendPlacementBlock(int orient) {
      return this.extendPlacementBlock[orient];
   }

   public ModelMetadata getMetadata() {
      return this.metadata;
   }

   public boolean canPlace(World world, int x, int y, int z, EntityPlayer player, int orient) {
      for(int lx = this.extendPlacementBlock[orient][0]; lx <= this.extendPlacementBlock[orient][3]; ++lx) {
         for(int ly = this.metadata.csmodel.getRootNode() != null && this.metadata.csmodel.getRootNode().hasAttribute(CSModel.ModelNode.Attrb.IGNOREBELOWYPLANE) ? 0 : this.extendPlacementBlock[orient][1]; ly <= this.extendPlacementBlock[orient][4]; ++ly) {
            for(int lz = this.extendPlacementBlock[orient][2]; lz <= this.extendPlacementBlock[orient][5]; ++lz) {
               Block targetBlock = world.getBlock(lx + x, ly + y, lz + z);
               if (targetBlock.getMaterial() != Material.air && targetBlock != Blocks.tallgrass && targetBlock != Blocks.snow_layer) {
                  return false;
               }
            }
         }
      }

      return true;
   }

   public static class NodeWrapper {
      protected final CSModel.ModelNode node;
      protected final CraftStudioModelWrapper modelWrapper;
      protected final boolean flat;
      protected Vector3[][] vertices = new Vector3[16][8];
      protected Vector3[][] extend = new Vector3[16][2];

      public NodeWrapper(CSModel.ModelNode node, CraftStudioModelWrapper modelWrapper) {
         this.node = node;
         this.modelWrapper = modelWrapper;
         short[] size = node.getSize();
         this.flat = size[0] == 0 || size[1] == 0 || size[2] == 0;
      }

      public void computeVertices(Matrix4 matrix, int orient) {
         Matrix4 pushed = matrix.cpy();
         float[] scaledSize = this.node.getSize(0.0625F);
         Matrix4 position = new Matrix4();
         position.setTranslation(this.node.getPosition()[0], -this.node.getPosition()[1], -this.node.getPosition()[2]);
         pushed.mul(position);
         Quaternion quat = this.node.getOrientation();
         quat.y *= -1.0F;
         quat.z *= -1.0F;
         pushed.mul(new Matrix4(quat));
         Matrix4 offset = new Matrix4();
         offset.setTranslation(this.node.getOffset()[0], this.node.getOffset()[1] * -1.0F, this.node.getOffset()[2] * -1.0F);
         pushed.mul(offset);
         Matrix4 mWorldScaling = pushed.cpy();
         Matrix4 scaling = new Matrix4();
         scaling.setToScaling(Math.abs(this.node.getScale()[0]), Math.abs(this.node.getScale()[1]), Math.abs(this.node.getScale()[2]));
         mWorldScaling.mul(scaling);
         this.vertices[orient][0] = (new Vector3(-scaledSize[0] / 2.0F, -scaledSize[1] / 2.0F, -scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][1] = (new Vector3(-scaledSize[0] / 2.0F, -scaledSize[1] / 2.0F, scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][2] = (new Vector3(-scaledSize[0] / 2.0F, scaledSize[1] / 2.0F, -scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][3] = (new Vector3(-scaledSize[0] / 2.0F, scaledSize[1] / 2.0F, scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][4] = (new Vector3(scaledSize[0] / 2.0F, -scaledSize[1] / 2.0F, -scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][5] = (new Vector3(scaledSize[0] / 2.0F, -scaledSize[1] / 2.0F, scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][6] = (new Vector3(scaledSize[0] / 2.0F, scaledSize[1] / 2.0F, -scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.vertices[orient][7] = (new Vector3(scaledSize[0] / 2.0F, scaledSize[1] / 2.0F, scaledSize[2] / 2.0F)).mul(mWorldScaling);
         this.extend[orient] = ProjectionHelper.getExtend(this.vertices[orient]);
         Iterator var10 = this.node.getChildren().iterator();

         while(var10.hasNext()) {
            CSModel.ModelNode child = (CSModel.ModelNode)var10.next();
            this.modelWrapper.nodeCache.get(child).computeVertices(pushed, orient);
         }

      }

      public Vector3[] getVertices(int orient) {
         return this.vertices[orient];
      }

      public Vector3[] getExtend(int orient) {
         return this.extend[orient];
      }

      public boolean isFlat() {
         return this.flat;
      }
   }

   public static class NodeHashCache extends HashMap {
      final CraftStudioModelWrapper modelWrapper;

      public NodeHashCache(CraftStudioModelWrapper modelWrapper) {
         this.modelWrapper = modelWrapper;
      }

      public CraftStudioModelWrapper.NodeWrapper get(Object key) {
         CraftStudioModelWrapper.NodeWrapper ret = (CraftStudioModelWrapper.NodeWrapper)super.get(key);
         if (ret == null) {
            ret = new CraftStudioModelWrapper.NodeWrapper((CSModel.ModelNode)key, this.modelWrapper);
            this.put((CSModel.ModelNode)key, ret);
         }

         return ret;
      }
   }
}
